//
// Created by hjie on 23-12-31.
//

#include "amix.h"
#include <cstring>
#include <libavfilter/buffersink.h>
#include <vector>

bool InitInputFile(std::vector<std::string> file_vector, AudioMix & mix_data)
{
    int index = 0;
    for (auto & data : file_vector)
    {
        auto input_file = std::make_shared<InputFile>();
        if (input_file->m_input_ctx.OpenUrlWithFindStreamInfo(data))
        {
            input_file->m_input_ctx.CreateAudioDecoderCtx("", CodecConfig{});
        }
        else
        {
            return false;
        }
        input_file->m_index = index;
        mix_data.m_input_file_map[index++] = input_file;
    }
    // init filter data
    return InitFilterGraph(mix_data);
}

bool InitOutputFile(AudioMix & mix_data, AVFrame * frame)
{
    std::string output_filename = "./test.aac";
    CodecConfig config;
    config.m_audio_config.m_codec_name = "aac";
    config.m_audio_config.m_format     = AV_SAMPLE_FMT_FLTP;
    bool result = mix_data.m_output_ctx.CreateUrl(output_filename);
    if (result)
    {
        config.m_audio_config.m_frame = frame;
        config.m_audio_config.m_config_callback = [&mix_data](AVCodecContext * codec_context){

            codec_context->sample_fmt  = (AVSampleFormat)av_buffersink_get_format(mix_data.m_buffer_sink_ctx);
            codec_context->time_base   = av_buffersink_get_time_base(mix_data.m_buffer_sink_ctx);
            codec_context->framerate   = av_buffersink_get_frame_rate(mix_data.m_buffer_sink_ctx);
            codec_context->sample_rate = av_buffersink_get_sample_rate(mix_data.m_buffer_sink_ctx);
            codec_context->channel_layout = av_buffersink_get_channel_layout(mix_data.m_buffer_sink_ctx);
            codec_context->channels    = av_buffersink_get_channels(mix_data.m_buffer_sink_ctx);
            

        };
        result = mix_data.m_output_ctx.CreateAudioStream(mix_data.m_input_file_map.begin()->second->m_input_ctx.AudioMediaStream(), config);
        if (result)
        {
            mix_data.m_output_ctx.WriterHeaderTailer();
        }
    }
    return result;
}

bool FilterProcessFrame(AudioMix & mix_data, std::shared_ptr<InputFile> input, AVFrame * frame)
{
    int result = av_buffersrc_add_frame_flags(input->m_buffer_filter_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
    if (result < 0)
    {
        PrintLog(result);
        return false;
    }
    int ret = true;
    while (true)
    {
        AVFrame * frame = av_frame_alloc();
        result = av_buffersink_get_frame(mix_data.m_buffer_sink_ctx, frame);
        if (result < 0)
        {
            ret = false;
            if (result == AVERROR_EOF || result == AVERROR(EAGAIN))
            {
                ret = true;
            }
            av_frame_free(&frame);
            break;
        }
        else
        {
            mix_data.m_filter_frame_queue.PushFrame(&frame);
        }
    }
    return ret;
}

bool InitFilterGraph(AudioMix & mix_data)
{
    mix_data.m_filter_graph = avfilter_graph_alloc();
    // amix
    char args[1024];
    memset(args, 0, sizeof(args));
    /* bzero(args, sizeof(args)); */
    sprintf(args, "inputs=%ld:duration=longest", mix_data.m_input_file_map.size());
    const AVFilter  * filter = avfilter_get_by_name("amix");
    AVFilterContext * amix_filter_ctx = nullptr;
    int result = avfilter_graph_create_filter(&amix_filter_ctx, filter, "amix_input", args, nullptr, mix_data.m_filter_graph);
    if (result < 0)
    {
        PrintLog(result);
        return false;
    }
    int index = 0;
    for (auto & data : mix_data.m_input_file_map)
    {
        std::string custom_name = std::string("input_") + std::to_string(data.first);
        filter = avfilter_get_by_name("abuffer");
        auto stream = data.second->m_input_ctx.AudioMediaStream();
        memset(args, 0, sizeof(args));
        /* bzero(args, sizeof(args)); */
        sprintf(args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%lu:channels=%d",
                 stream->AvStream()->time_base.num,
                 stream->AvStream()->time_base.den,
                 data.second->m_input_ctx.AudioDecoder()->AvCodecCtx()->sample_rate,
                 av_get_sample_fmt_name((AVSampleFormat)stream->AvStream()->codecpar->format),
                 stream->AvStream()->codecpar->channel_layout,
                 stream->AvStream()->codecpar->channels);

        printf("index : %d\tabuffer parameter :%s.\n", data.first, args);
        data.second->m_buffer_filter_ctx = nullptr;

        result = avfilter_graph_create_filter(&data.second->m_buffer_filter_ctx, filter, custom_name.c_str(), args, nullptr, mix_data.m_filter_graph);
        if (result < 0)
        {
            PrintLog(result);
            return false;
        }
        result = avfilter_link(data.second->m_buffer_filter_ctx, 0, amix_filter_ctx, index++);
        if (result < 0)
        {
            PrintLog(result);
            return false;
        }
    }
    filter = avfilter_get_by_name("aformat");
    AVFilterContext * filter_format_ctx = nullptr;
    memset(args, 0, sizeof(args));
    /* bzero(args, sizeof(args)); */
    sprintf(args, "sample_rates=48000:sample_fmts=%s:channel_layouts=3", av_get_sample_fmt_name(AV_SAMPLE_FMT_FLTP));
    printf("%s.\n", args);
    result = avfilter_graph_create_filter(&filter_format_ctx, filter, "format_0", args, nullptr, mix_data.m_filter_graph);
    if (result < 0)
    {
        PrintLog(result);
        return false;
    }
    avfilter_link(amix_filter_ctx, 0,filter_format_ctx, 0);
    // abuffer sink
    filter = avfilter_get_by_name("abuffersink");
    result = avfilter_graph_create_filter(&mix_data.m_buffer_sink_ctx, filter, "out", nullptr, nullptr, mix_data.m_filter_graph);
    if (result < 0)
    {
        PrintLog(result);
        return false;
    }
    result = avfilter_link(filter_format_ctx, 0, mix_data.m_buffer_sink_ctx, 0);
    if (result < 0)
    {
        PrintLog(result);
        return false;
    }
    result = avfilter_graph_config(mix_data.m_filter_graph, nullptr);
    if (result < 0)
    {
        PrintLog(result);
        return false;
    }
    av_buffersink_set_frame_size(mix_data.m_buffer_sink_ctx, 1024);
    char * ptr = avfilter_graph_dump(mix_data.m_filter_graph, nullptr);
    if (ptr)
    {
        printf("%s.\n", ptr);
        av_free(ptr);
    }
    // dump graph information
    mix_data.m_filter_frame_queue.InitMessageQueue(1000);
    mix_data.m_encode_packet_queue.InitMessageQueue(1000);
    return true;
}
